﻿namespace IESN.Communication
{
    using Microsoft.VisualBasic; 
    using System;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Threading;
    using Microsoft.VisualBasic.CompilerServices;

    public class Rs232
    {
        private const int ERROR_IO_INCOMPLETE = 0x3e4;
        private const int ERROR_IO_PENDING = 0x3e5;
        private const int FILE_FLAG_OVERLAPPED = 0x40000000;
        private const int GENERIC_READ = -2147483648;
        private const int GENERIC_WRITE = 0x40000000;
        private const int INFINITE = -1;
        private const int INVALID_HANDLE_VALUE = -1;
        private const int IO_BUFFER_SIZE = 0x400;
        private byte[] mabtRxBuf;
        private byte[] mabtTmpTxBuf;
        private bool mbWaitOnRead;
        private bool mbWaitOnWrite;
        private bool mbWriteErr;
        private EventMasks meMask;
        private Mode meMode;
        private DataParity meParity = DataParity.Parity_None;
        private DataStopBit meStopBit = ((DataStopBit) 0);
        private int mhRS = -1;
        private int miBaudRate = 0x2580;
        private int miBufferSize = 0x400;
        private int miDataBit = 8;
        private int miPort = 1;
        private int miTimeout = 100;
        private int miTmpBytes2Read;
        private Thread moThreadRx;
        private Thread moThreadTx;
        private OVERLAPPED muOverlapped;
        private OVERLAPPED muOverlappedE;
        private OVERLAPPED muOverlappedW;
        private const int OPEN_EXISTING = 3;
        private const int PURGE_RXABORT = 2;
        private const int PURGE_RXCLEAR = 8;
        private const int PURGE_TXABORT = 1;
        private const int PURGE_TXCLEAR = 4;
        private const int WAIT_OBJECT_0 = 0;
        private const int WAIT_TIMEOUT = 0x102;

        public event CommEventEventHandler CommEventEvent;

        public event DataReceivedEventHandler DataReceivedEvent;

        public event TxCompletedEventHandler TxCompletedEvent;

        public void _R()
        {
            int num = this.Read(this.miTmpBytes2Read);
        }

        public void _W()
        {
            this.Write(this.mabtTmpTxBuf);
        }

        public void AsyncRead(int Bytes2Read)
        {
            if (this.meMode != Mode.Overlapped)
            {
                throw new ApplicationException("Async Methods allowed only when WorkingMode=Overlapped");
            }
            this.miTmpBytes2Read = Bytes2Read;
            this.moThreadTx = new Thread(new ThreadStart(this._R));
            this.moThreadTx.Start();
        }

        public void AsyncWrite(byte[] Buffer)
        {
            if (this.meMode != Mode.Overlapped)
            {
                throw new ApplicationException("Async Methods allowed only when WorkingMode=Overlapped");
            }
            if (this.mbWaitOnWrite)
            {
                throw new ApplicationException("Unable to send message because of pending transmission.");
            }
            this.mabtTmpTxBuf = Buffer;
            this.moThreadTx = new Thread(new ThreadStart(this._W));
            this.moThreadTx.Start();
        }

        public void AsyncWrite(string Buffer)
        {
            byte[] bytes = new ASCIIEncoding().GetBytes(Buffer);
            this.AsyncWrite(bytes);
        }

        [DllImport("kernel32.dll")]
        private static extern int BuildCommDCB(string lpDef, ref DCB lpDCB);
        public bool CheckLineStatus(ModemStatusBits Line)
        {
            return Convert.ToBoolean((int) (this.ModemStatus & Line));
        }

        [DllImport("kernel32.dll")]
        private static extern int ClearCommError(int hFile, int lpErrors, int l);
        public void ClearInputBuffer()
        {
            if (this.mhRS != -1)
            {
                PurgeComm(this.mhRS, 8);
            }
        }

        public void Close()
        {
            if (this.mhRS != -1)
            {
                CloseHandle(this.mhRS);
                this.mhRS = -1;
            }
        }

        [DllImport("kernel32.dll")]
        private static extern int CloseHandle(int hObject);
        [DllImport("kernel32.dll")]
        private static extern int CreateEvent(int lpEventAttributes, int bManualReset, int bInitialState, [MarshalAs(UnmanagedType.LPStr)] string lpName);
        [DllImport("kernel32.dll")]
        private static extern int CreateFile([MarshalAs(UnmanagedType.LPStr)] string lpFileName, int dwDesiredAccess, int dwShareMode, int lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, int hTemplateFile);
        [DllImport("kernel32.dll")]
        private static extern bool EscapeCommFunction(int hFile, long ifunc);
        [DllImport("kernel32.dll")]
        private static extern int FormatMessage(int dwFlags, int lpSource, int dwMessageId, int dwLanguageId, [MarshalAs(UnmanagedType.LPStr)] string lpBuffer, int nSize, int Arguments);
        [DllImport("kernel32", EntryPoint="FormatMessageA", CharSet=CharSet.Ansi, SetLastError=true, ExactSpelling=true)]
        private static extern int FormatMessage(int dwFlags, int lpSource, int dwMessageId, int dwLanguageId, StringBuilder lpBuffer, int nSize, int Arguments);
        [DllImport("kernel32.dll")]
        public static extern bool GetCommModemStatus(int hFile, ref int lpModemStatus);
        [DllImport("kernel32.dll")]
        private static extern int GetCommState(int hCommDev, ref DCB lpDCB);
        [DllImport("kernel32.dll")]
        private static extern int GetCommTimeouts(int hFile, ref COMMTIMEOUTS lpCommTimeouts);
        [DllImport("kernel32.dll")]
        private static extern int GetLastError();
        [DllImport("kernel32.dll")]
        private static extern int GetOverlappedResult(int hFile, ref OVERLAPPED lpOverlapped, ref int lpNumberOfBytesTransferred, int bWait);
        public void Open()
        {
            int dwFlagsAndAttributes = Convert.ToInt32(RuntimeHelpers.GetObjectValue(Interaction.IIf(this.meMode == Mode.Overlapped, 0x40000000, 0)));
            if (this.miPort <= 0)
            {
                throw new ApplicationException("COM Port not defined, use Port property to set it before invoking InitPort");
            }
            try
            {
                DCB dcb=new DCB();
                int num3=0;
                this.mhRS = CreateFile("COM" + this.miPort.ToString(), -1073741824, 0, 0, 3, dwFlagsAndAttributes, 0);
                if (this.mhRS == -1)
                {
                    throw new CIOChannelException("Unable to open COM" + this.miPort.ToString());
                }
                int commState = ClearCommError(this.mhRS, num3, 0);
                commState = PurgeComm(this.mhRS, 12);
                commState = GetCommState(this.mhRS, ref dcb);
                string str2 = "NOEM";
                str2 = str2.Substring((int) this.meParity, 1);
                commState = BuildCommDCB(string.Format("baud={0} parity={1} data={2} stop={3}", new object[] { this.miBaudRate, str2, this.miDataBit, (int) this.meStopBit }), ref dcb);
                if (SetCommState(this.mhRS, ref dcb) == 0)
                {
                    string str3 = this.pErr2Text(GetLastError());
                    throw new CIOChannelException("Unable to set COM state0" + str3);
                }
                commState = SetupComm(this.mhRS, this.miBufferSize, this.miBufferSize);
                this.pSetTimeout();
            }
            catch (Exception exception1)
            {
                ProjectData.SetProjectError(exception1);
                Exception innerException = exception1;
                throw new CIOChannelException(innerException.Message, innerException);
            }
        }

        public void Open(int Port, int BaudRate, int DataBit, DataParity Parity, DataStopBit StopBit, int BufferSize)
        {
            this.Port = Port;
            this.BaudRate = BaudRate;
            this.DataBit = DataBit;
            this.Parity = Parity;
            this.StopBit = StopBit;
            this.BufferSize = BufferSize;
            this.Open();
        }

        private string pErr2Text(int lCode)
        {
            StringBuilder lpBuffer = new StringBuilder(0x100);
            if (FormatMessage(0x1000, 0, lCode, 0, lpBuffer, 0x100, 0) > 0)
            {
                return lpBuffer.ToString();
            }
            return "Error not found.";
        }

        private void pHandleOverlappedRead(int Bytes2Read)
        {
            int lastError=0;
            int num3=0;
            this.muOverlapped.hEvent = CreateEvent(0, 1, 0, null);
            if (this.muOverlapped.hEvent == 0)
            {
                throw new ApplicationException("Error creating event for overlapped read.");
            }
            if (!this.mbWaitOnRead)
            {
                this.mabtRxBuf = new byte[(Bytes2Read - 1) + 1];
                if (ReadFile(this.mhRS, this.mabtRxBuf, Bytes2Read, ref num3, ref this.muOverlapped) == 0)
                {
                    lastError = GetLastError();
                    if (lastError != 0x3e5)
                    {
                        throw new ArgumentException("Overlapped Read Error: " + this.pErr2Text(lastError));
                    }
                    this.mbWaitOnRead = true;
                }
                else if (this.DataReceivedEvent != null)
                {
                    this.DataReceivedEvent(this, this.mabtRxBuf);
                }
            }
            if (this.mbWaitOnRead)
            {
                switch (WaitForSingleObject(this.muOverlapped.hEvent, this.miTimeout))
                {
                    case 0:
                        if (GetOverlappedResult(this.mhRS, ref this.muOverlapped, ref num3, 0) == 0)
                        {
                            lastError = GetLastError();
                            if (lastError == 0x3e4)
                            {
                                throw new ApplicationException("Read operation incomplete");
                            }
                            throw new ApplicationException("Read operation error " + lastError.ToString());
                        }
                        if (this.DataReceivedEvent != null)
                        {
                            this.DataReceivedEvent(this, this.mabtRxBuf);
                        }
                        this.mbWaitOnRead = false;
                        goto Label_015F;

                    case 0x102:
                        throw new IOTimeoutException("Timeout error");
                }
                throw new ApplicationException("Overlapped read error");
            }
        Label_015F:
            CloseHandle(this.muOverlapped.hEvent);
        }

        private bool pHandleOverlappedWrite(byte[] Buffer)
        {
            bool flag=false;
            int num=0;
            this.muOverlappedW.hEvent = CreateEvent(0, 1, 0, null);
            if (this.muOverlappedW.hEvent == 0)
            {
                throw new ApplicationException("Error creating event for overlapped write.");
            }
            PurgeComm(this.mhRS, 12);
            this.mbWaitOnRead = true;
            if (WriteFile(this.mhRS, Buffer, Buffer.Length, ref num, ref this.muOverlappedW) == 0)
            {
                int lastError = GetLastError();
                if (lastError != 0x3e5)
                {
                    throw new ArgumentException("Overlapped Read Error: " + this.pErr2Text(lastError));
                }
                if (WaitForSingleObject(this.muOverlappedW.hEvent, -1) == 0)
                {
                    if (GetOverlappedResult(this.mhRS, ref this.muOverlappedW, ref num, 0) == 0)
                    {
                        flag = true;
                    }
                    else
                    {
                        this.mbWaitOnRead = false;
                        if (this.TxCompletedEvent != null)
                        {
                            this.TxCompletedEvent(this);
                        }
                    }
                }
            }
            else
            {
                flag = false;
            }
            CloseHandle(this.muOverlappedW.hEvent);
            return flag;
        }

        private void pSetTimeout()
        {
            if (this.mhRS != -1)
            {
                COMMTIMEOUTS commtimeouts;
                commtimeouts.ReadIntervalTimeout = 0;
                commtimeouts.ReadTotalTimeoutMultiplier = 0;
                commtimeouts.ReadTotalTimeoutConstant = this.miTimeout;
                commtimeouts.WriteTotalTimeoutMultiplier = 10;
                commtimeouts.WriteTotalTimeoutConstant = 100;
                SetCommTimeouts(this.mhRS, ref commtimeouts);
            }
        }

        [DllImport("kernel32.dll")]
        private static extern int PurgeComm(int hFile, int dwFlags);
        public int Read(int Bytes2Read)
        {
            int num3=0;
            if (Bytes2Read == 0)
            {
                Bytes2Read = this.miBufferSize;
            }
            if (this.mhRS == -1)
            {
                throw new ApplicationException("Please initialize and open port before using this method");
            }
            try
            {
                int num2=0;
                if (this.meMode == Mode.Overlapped)
                {
                    this.pHandleOverlappedRead(Bytes2Read);
                    return num3;
                }
                this.mabtRxBuf = new byte[(Bytes2Read - 1) + 1];
                OVERLAPPED lpOverlapped = new OVERLAPPED();//0;
                int num = ReadFile(this.mhRS, this.mabtRxBuf, Bytes2Read, ref num2, ref lpOverlapped);
                if (num == 0)
                {
                    throw new ApplicationException("ReadFile error " + num.ToString());
                }
                if (num2 < Bytes2Read)
                {
                    throw new IOTimeoutException("Timeout error");
                }
                this.mbWaitOnRead = true;
                num3 = num2;
            }
            catch (Exception exception1)
            {
                ProjectData.SetProjectError(exception1);
                Exception innerException = exception1;
                throw new ApplicationException("Read Error: " + innerException.Message, innerException);
            }
            return num3;
        }

        [DllImport("kernel32.dll")]
        private static extern int ReadFile(int hFile, byte[] Buffer, int nNumberOfBytesToRead, ref int lpNumberOfBytesRead, ref OVERLAPPED lpOverlapped);
        [DllImport("kernel32.dll")]
        private static extern int SetCommMask(int hFile, int lpEvtMask);
        [DllImport("kernel32.dll")]
        private static extern int SetCommState(int hCommDev, ref DCB lpDCB);
        [DllImport("kernel32.dll")]
        private static extern int SetCommTimeouts(int hFile, ref COMMTIMEOUTS lpCommTimeouts);
        [DllImport("kernel32.dll")]
        private static extern int SetupComm(int hFile, int dwInQueue, int dwOutQueue);
        [DllImport("kernel32.dll")]
        private static extern int WaitCommEvent(int hFile, ref EventMasks Mask, ref OVERLAPPED lpOverlap);
        [DllImport("kernel32.dll")]
        private static extern int WaitForSingleObject(int hHandle, int dwMilliseconds);
        public void Write(byte[] Buffer)
        {
            if (this.mhRS == -1)
            {
                throw new ApplicationException("Please initialize and open port before using this method");
            }
            try
            {
                if (this.meMode == Mode.Overlapped)
                {
                    if (this.pHandleOverlappedWrite(Buffer))
                    {
                        throw new ApplicationException("Error in overllapped write");
                    }
                }
                else
                {
                    int num=0;
                    PurgeComm(this.mhRS, 12);
                    OVERLAPPED lpOverlapped = new OVERLAPPED();//0;
                    if (WriteFile(this.mhRS, Buffer, Buffer.Length, ref num, ref lpOverlapped) == 0)
                    {
                        throw new ApplicationException("Write Error - Bytes Written " + num.ToString() + " of " + Buffer.Length.ToString());
                    }
                }
            }
            catch (Exception exception1)
            {
                ProjectData.SetProjectError(exception1);
                Exception exception = exception1;
                throw;
            }
        }

        public void Write(string Buffer)
        {
            byte[] bytes = new ASCIIEncoding().GetBytes(Buffer);
            this.Write(bytes);
        }

        [DllImport("kernel32.dll")]
        private static extern int WriteFile(int hFile, byte[] Buffer, int nNumberOfBytesToWrite, ref int lpNumberOfBytesWritten, ref OVERLAPPED lpOverlapped);

        public int BaudRate
        {
            get
            {
                return this.miBaudRate;
            }
            set
            {
                this.miBaudRate = value;
            }
        }

        public int BufferSize
        {
            get
            {
                return this.miBufferSize;
            }
            set
            {
                this.miBufferSize = value;
            }
        }

        public int DataBit
        {
            get
            {
                return this.miDataBit;
            }
            set
            {
                this.miDataBit = value;
            }
        }

        public bool Dtr
        {
            set
            {
                if (this.mhRS != -1)
                {
                    if (value)
                    {
                        EscapeCommFunction(this.mhRS, 5L);
                    }
                    else
                    {
                        EscapeCommFunction(this.mhRS, 6L);
                    }
                }
            }
        }

        public virtual byte[] InputStream
        {
            get
            {
                return this.mabtRxBuf;
            }
        }

        public virtual string InputStreamString
        {
            get
            {
                ASCIIEncoding encoding = new ASCIIEncoding();
                string str2 = "";
                try
                {
                    str2 = encoding.GetString(this.InputStream);
                }
                catch (Exception exception1)
                {
                    ProjectData.SetProjectError(exception1);
                    str2 = "";
                    ProjectData.ClearProjectError();
                }
                return str2;
            }
        }

        public bool IsOpen
        {
            get
            {
                return (this.mhRS != -1);
            }
        }

        public ModemStatusBits ModemStatus
        {
            get
            {
                int num=0;
                if (this.mhRS == -1)
                {
                    throw new ApplicationException("Please initialize and open port before using this method");
                }
                if (!GetCommModemStatus(this.mhRS, ref num))
                {
                    throw new ApplicationException("Unable to get modem status");
                }
                return (ModemStatusBits) num;
            }
        }

        public DataParity Parity
        {
            get
            {
                return this.meParity;
            }
            set
            {
                this.meParity = value;
            }
        }

        public int Port
        {
            get
            {
                return this.miPort;
            }
            set
            {
                this.miPort = value;
            }
        }

        public bool Rts
        {
            set
            {
                if (this.mhRS != -1)
                {
                    if (value)
                    {
                        EscapeCommFunction(this.mhRS, 3L);
                    }
                    else
                    {
                        EscapeCommFunction(this.mhRS, 4L);
                    }
                }
            }
        }

        public DataStopBit StopBit
        {
            get
            {
                return this.meStopBit;
            }
            set
            {
                this.meStopBit = value;
            }
        }

        public virtual int Timeout
        {
            get
            {
                return this.miTimeout;
            }
            set
            {
                this.miTimeout = IntegerType.FromObject(Interaction.IIf(value == 0, 500, value));
                this.pSetTimeout();
            }
        }

        public Mode WorkingMode
        {
            get
            {
                return this.meMode;
            }
            set
            {
                this.meMode = value;
            }
        }

        public class CIOChannelException : ApplicationException
        {
            public CIOChannelException(string Message) : base(Message)
            {
            }

            public CIOChannelException(string Message, Exception InnerException) : base(Message, InnerException)
            {
            }
        }

        [StructLayout(LayoutKind.Sequential, Pack=1)]
        private struct COMMCONFIG
        {
            public int dwSize;
            public short wVersion;
            public short wReserved;
            public Rs232.DCB dcbx;
            public int dwProviderSubType;
            public int dwProviderOffset;
            public int dwProviderSize;
            public byte wcProviderData;
        }

        public delegate void CommEventEventHandler(Rs232 Source, Rs232.EventMasks Mask);

        [StructLayout(LayoutKind.Sequential, Pack=1)]
        private struct COMMTIMEOUTS
        {
            public int ReadIntervalTimeout;
            public int ReadTotalTimeoutMultiplier;
            public int ReadTotalTimeoutConstant;
            public int WriteTotalTimeoutMultiplier;
            public int WriteTotalTimeoutConstant;
        }

        public enum DataParity
        {
            Parity_None,
            Pariti_Odd,
            Parity_Even,
            Parity_Mark
        }

        public delegate void DataReceivedEventHandler(Rs232 Source, byte[] DataBuffer);

        public enum DataStopBit
        {
            StopBit_1 = 1,
            StopBit_2 = 2
        }

        [StructLayout(LayoutKind.Sequential, Pack=1)]
        private struct DCB
        {
            public int DCBlength;
            public int BaudRate;
            public int Bits1;
            public short wReserved;
            public short XonLim;
            public short XoffLim;
            public byte ByteSize;
            public byte Parity;
            public byte StopBits;
            public byte XonChar;
            public byte XoffChar;
            public byte ErrorChar;
            public byte EofChar;
            public byte EvtChar;
            public short wReserved2;
        }

        [Flags]
        public enum EventMasks
        {
            Break = 0x40,
            ClearToSend = 8,
            DataSetReady = 0x10,
            ReceiveLine = 0x20,
            Ring = 0x100,
            RxChar = 1,
            RXFlag = 2,
            StatusError = 0x80,
            TxBufferEmpty = 4
        }

        public class IOTimeoutException : Rs232.CIOChannelException
        {
            public IOTimeoutException(string Message) : base(Message)
            {
            }

            public IOTimeoutException(string Message, Exception InnerException) : base(Message, InnerException)
            {
            }
        }

        private enum Lines
        {
            ClearBreak = 9,
            ClearDtr = 6,
            ClearRts = 4,
            ResetDev = 7,
            SetBreak = 8,
            SetDtr = 5,
            SetRts = 3
        }

        public enum Mode
        {
            NonOverlapped,
            Overlapped
        }

        [Flags]
        public enum ModemStatusBits
        {
            CarrierDetect = 0x80,
            ClearToSendOn = 0x10,
            DataSetReadyOn = 0x20,
            RingIndicatorOn = 0x40
        }

        [StructLayout(LayoutKind.Sequential, Pack=1)]
        public struct OVERLAPPED
        {
            public int Internal;
            public int InternalHigh;
            public int Offset;
            public int OffsetHigh;
            public int hEvent;
        }

        private enum PurgeBuffers
        {
            RXAbort = 2,
            RXClear = 8,
            TxAbort = 1,
            TxClear = 4
        }

        public delegate void TxCompletedEventHandler(Rs232 Source);
    }
}

